/*!
 *  Simple Javascript Template Engine v1.0
 *  http://code.google.com/p/uri/
 *  
 *  Copyright 2010, Li Qiang, liqiang@gmail.com
 *  Dual licensed under the MIT or any OSI licenses.
 */

/**
 * Place template in script block:
 * 
 * <script id="template1" type="text/x-blacktea-template">
 *    <span>Name: ${name}</span>
 *    <span>MI: ${MI}</span>
 *    <span>Formatted Name: ${fn:formattedName}</span>
 * </script>
 * 
 * Use template as follows:
 * 
 * blacktea(tid)(data, fn, br, mpl) or blacktea(tid, data, fn, br, mpl)
 * 
 * line break (br) and max per line (mpl) are ignored if data is not an array.
 * 
 * var tpl = blacktea('template1');
 * var data = { name: 'value', MI: function () { return this.name.charAt(0).toUpperCase(); } };
 * var formatter = { formattedName: function () {return 'Dr. ' + this.name;} };
 *
 * var result = tpl(data, formatter); or // blacktea('template1', data, formatter);
 * 
 * All occurrences of ${name} or ${fn:name} in template will be replaced with values in data/formatter. 
 * functions can be used in data and formatter including blacktea itself.
 * 
 */

/*jslint undef: true, confusion: true, unparam: true */ 
(function (window) {
	'use strict';
	var doc = window.document, cache = {};
	
	function template(tid) {
		var str = doc.getElementById(tid).innerHTML;
		//normalize
		str = str ? str.replace(/^\s+/, '').replace(/\s+$/, '').replace(/[\r\t\n]/g, ' ') : '';
		
		function tpl(data, fn) {
			var vcache = {}, fcache = {};
			
			function invoke(v) {
				switch (typeof v) {
				case 'undefined':
					return ''; 
				case 'string':
					return v;
				case 'function':
					return v.call(data); 
				default:
					return (v === null ? '' : v.toString());
				}
			}
			
			// ${name} or ${fn:name}
			return str.replace(/\$\{\s*(fn:)?([a-zA-Z_][a-zA-Z0-9_.]*)\s*\}/g, function (s, f, n) {
				var v = f ? fcache[n] : vcache[n];
				if (typeof v !== 'undefined') {
					return v;
				}
				if (f) {
					v = fn ? invoke(fn[n]) : '';
					fcache[n] = v;
				} else {
					v = data ? invoke(data[n]) : '';
					vcache[n] = v;
				}
				return v;
			});
		}
		
		function grid(data, fn, br, mpl) {
			var html = [], sep = (br && mpl), len = data.length, i;
			for (i = 0; i < len; i += 1) {
				if (sep) {
					html.push(i > 0 && i % mpl === 0 ? br : '');
				}
				html.push(tpl(data[i], fn));
			}
			return html.join('');
		}
		
		return function (data, fn, br, mpl) {
			return (data && typeof data.length === 'number' ? grid(data, fn, br, mpl) : tpl(data, fn));
		};
	}

	window.blacktea = function (tid, data, fn, br, mpl) {
		var t = cache[tid];
		if (!t) {
			t = template(tid);
			cache[tid] = t;
		}
		return data ? t(data, fn, br, mpl) : t;
	};

}(window));
